package it.unibo.refolding.pico.batch;

import it.unibo.refolding.common.MyRuntimeException;
import it.unibo.refolding.pico.RetractionCurve;

import java.io.IOException;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.io.output.NullWriter;
import org.apache.commons.lang3.StringUtils;

import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableSet;

public class AmnesticRetractionCurveBatch extends RetractionCurveBatchBase {

  private final Writer retractionPlaylistWriter;
  private final Writer peakCsvWriter;
  private final Writer filteredRetractionPlaylistWriter;
  private final Writer hookePlaylistWriter;
  private final Writer filteredHookePlaylistWriter;
  private int peakCsvIndex;
  private int size;

  public AmnesticRetractionCurveBatch(int iteration, Path retractionPlaylistPath, Path peakCsvPath,
    Path hookePlaylistPath, double clRangeStartM, double clRangeEndM, int wellSpacedAtEnd,
    int allowSkipped, Path filteredRetractionPlaylistPath, Path filteredHookePlaylistPath) {
    super(
      iteration, retractionPlaylistPath, peakCsvPath, hookePlaylistPath, clRangeStartM, clRangeEndM,
      wellSpacedAtEnd, allowSkipped, filteredRetractionPlaylistPath, filteredHookePlaylistPath);
    this.retractionPlaylistWriter = writer(retractionPlaylistPath);
    this.peakCsvWriter = writer(peakCsvPath);
    this.hookePlaylistWriter = writer(hookePlaylistPath);
    this.filteredRetractionPlaylistWriter = writer(filteredRetractionPlaylistPath);
    this.filteredHookePlaylistWriter = writer(filteredHookePlaylistPath);
    peakCsvIndex = 0;
    try {
      hookePlaylistWriter.write(BatchUtil.HOOKE_HEADER);
      filteredHookePlaylistWriter.write(BatchUtil.HOOKE_HEADER);
    } catch (IOException e) {
      throw new MyRuntimeException(e);
    }
  }

  private static Writer writer(Path path) {
    if (StringUtils.isEmpty(path.toString())) {
      return NullWriter.NULL_WRITER;
    } else {
      try {
        return Files.newBufferedWriter(path, Charsets.ISO_8859_1);
      } catch (IOException e) {
        throw new MyRuntimeException("Could not create writer for: " + path, e);
      }
    }
  }

  @Override
  public void load() {
  }

  @Override
  public int size() {
    return size;
  }

  @Override
  public boolean containsKey(Path key) {
    return false;
  }

  @Override
  public RetractionCurve put(Path key, RetractionCurve value) {
    try {
      // Optimisation.
      if (retractionPlaylistWriter != NullWriter.NULL_WRITER) {
        synchronized (retractionPlaylistWriter) {
          BatchUtil.writeIfNecessary(
            0, Double.POSITIVE_INFINITY, 0, 0, value, iteration, retractionPlaylistWriter);
        }
      }
      // Optimisation.
      if (peakCsvWriter != NullWriter.NULL_WRITER) {
        synchronized (peakCsvWriter) {
          BatchUtil.writePeakCsvEntry(peakCsvIndex, value, iteration, peakCsvWriter);
          ++peakCsvIndex;
        }
      }
      // Optimisation.
      if (hookePlaylistWriter != NullWriter.NULL_WRITER) {
        synchronized (hookePlaylistWriter) {
          BatchUtil.writeHookeElement(0, Double.POSITIVE_INFINITY, 0, 0, value, iteration, hookePlaylistWriter);
        }
      }
      // Optimisation.
      if (filteredRetractionPlaylistWriter != NullWriter.NULL_WRITER) {
        synchronized (filteredRetractionPlaylistWriter) {
          BatchUtil.writeIfNecessary(
            clRangeStartM, clRangeEndM, wellSpacedAtEnd, allowSkipped, value, iteration,
            filteredRetractionPlaylistWriter);
        }
      }
      // Optimisation.
      if (filteredHookePlaylistWriter != NullWriter.NULL_WRITER) {
        synchronized (filteredHookePlaylistWriter) {
          BatchUtil.writeHookeElement(
            clRangeStartM, clRangeEndM, wellSpacedAtEnd, allowSkipped, value, iteration,
            filteredHookePlaylistWriter);
        }
      }
    } catch (IOException e) {
      throw new MyRuntimeException(e);
    }
    ++size;
    return null;
  }

  @Override
  public RetractionCurve get(Path key) {
    return null;
  }

  /**
   * It should be called such that it happens-after all the put calls.
   */
  @Override
  public void write() {
    try {
      retractionPlaylistWriter.close();
      peakCsvWriter.close();
      hookePlaylistWriter.write(BatchUtil.HOOKE_FOOTER);
      hookePlaylistWriter.close();
      filteredRetractionPlaylistWriter.close();
      filteredHookePlaylistWriter.write(BatchUtil.HOOKE_FOOTER);
      filteredHookePlaylistWriter.close();
    } catch (IOException e) {
      throw new MyRuntimeException(e);
    }
  }

  @Override
  public Set<Path> keySet() {
    return ImmutableSet.<Path>of();
  }

  @Override
  public Set<Entry<Path, RetractionCurve>> entrySet() {
    return ImmutableSet.<Entry<Path, RetractionCurve>>of();
  }
}
